package com.swak.mmap;

import java.nio.ByteBuffer;

import com.swak.utils.Bytes;
import com.swak.utils.time.DateTimes;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * 
 * MappedFileRecord.
 * 
 * 文件内容必须是格式化的才能知道文件写到了何处，下次恢复时才能追加文件.
 * 
 * MAGIC(2) + LEN(4) + TimeStamp(8) + Data
 * 
 * @author DELL
 */
@Getter
@Setter
@Accessors(chain = true)
public class MappedRecord {

	protected static final short MAGIC = (short) 0xbabe;
	protected static final byte MAGIC_HIGH = Bytes.short2bytes(MAGIC)[0];
	protected static final byte MAGIC_LOW = Bytes.short2bytes(MAGIC)[1];
	protected static final int HEADER_LENGTH = 14;
	protected static final long BLANK_RECORD_IDENT = -1;

	private final int dataLength;
	private final long timeStamp;
	private byte[] data;

	public MappedRecord(int dataLength, long timeStamp) {
		this.dataLength = dataLength;
		this.timeStamp = timeStamp;
	}

	public MappedRecord(long timeStamp, byte[] data) {
		this.dataLength = data.length;
		this.timeStamp = timeStamp;
		this.data = data;
	}

	public MappedRecord(byte[] data) {
		this.dataLength = data.length;
		this.timeStamp = DateTimes.nowMilli();
		this.data = data;
	}

	public int headerLength() {
		return HEADER_LENGTH;
	}

	public int dataLength() {
		return this.dataLength;
	}

	public int recordLength() {
		return HEADER_LENGTH + this.dataLength;
	}

	public void encode(ByteBuffer buffer) {
		buffer.position(0);
		buffer.limit(this.recordLength());
		buffer.putShort(MAGIC);
		buffer.putInt(dataLength);
		buffer.putLong(timeStamp);
		if (data != null) {
			buffer.put(data);
		}
	}

	public boolean isBlankRecord() {
		return this.timeStamp == BLANK_RECORD_IDENT;
	}

	public static MappedRecord decodeHeader(ByteBuffer buffer) {
		int readable = buffer.remaining();
		if (readable < HEADER_LENGTH) {
			return null;
		}

		short magic = buffer.getShort();
		if (magic != MAGIC) {
			return null;
		}
		int len = buffer.getInt();
		long timeStamp = buffer.getLong();
		return new MappedRecord(len, timeStamp);
	}

	public static MappedRecord decode(ByteBuffer buffer) {
		MappedRecord record = MappedRecord.decodeHeader(buffer);
		if (record != null && buffer.limit() >= record.dataLength) {
			byte[] datas = new byte[record.dataLength];
			buffer.get(datas);
			record.setData(datas);
		}
		return record;
	}

	/**
	 * 空白数据的特征： timeStamp == -1
	 * 
	 * @param maxBlank
	 * @return
	 */
	public MappedRecord toBlankRecord(int maxBlank) {
		return new MappedRecord(maxBlank - HEADER_LENGTH, -1).setData(null);
	}
}